#include "stm32f40x_gpio.h"
#include "stm32f40x_i2c.h"
#include "stm32f40x_rcc.h"
#include "stm32f40x_adc.h"
#include "stm32f40x_dma.h"

#include "global_conf.h"
#include "temperature.h"

static error_callback_t     temperature_callback;
static unsigned long adc_value = 0;

static void i2c_init(void);
static void i2c_start(unsigned char address, enum X_I2C_MODE mode);
static void i2c_stop(unsigned address);
static void i2c_close(void);
static unsigned char i2c_receive_byte_ack(void);
static unsigned char i2c_receive_byte_nack(void);
static unsigned char i2c_send_byte(const unsigned char ucdata);
static unsigned long i2c_read(unsigned char slave_address, unsigned char from_address, unsigned long length, unsigned char *pdata);
static unsigned long i2c_write(unsigned char slave_address, unsigned char from_address, unsigned long length, unsigned char *pdata);

static void i2c_stop(unsigned address)
{
    unsigned long timeout = TEMPERATURE_RETRY;
    /* Send STOP Condition after last byte has been transmitted */
    I2C_GenerateSTOP(I2C_USED, ENABLE);
    /* wait for EV8_2 --> byte has been transmitted */
    while(!I2C_CheckEvent(I2C_USED, I2C_EVENT_MASTER_BYTE_TRANSMITTED) ) {
        if (!(timeout--)) {
            temperature_callback(TEMP_ERROR);
        }
    }
}

unsigned char i2c_receive_byte_ack(void)
{
    unsigned long timeout = TEMPERATURE_RETRY;
    /* Enable acknowledge of received data */
    I2C_AcknowledgeConfig(I2C_USED, ENABLE);
    /* Wait until one byte has been received */
    while( !I2C_CheckEvent(I2C_USED, I2C_EVENT_MASTER_BYTE_RECEIVED)) {
        if (!(timeout--)) {
            temperature_callback(TEMP_ERROR);
        }
    }
    /* Read data from I2C data register and return data byte */
    unsigned char data = (unsigned char) I2C_ReceiveData(I2C_USED);
    return data;
}

static unsigned char i2c_receive_byte_nack(void)
{
     unsigned long timeout = TEMPERATURE_RETRY;
    /* Disable acknowledge of received data */
    I2C_AcknowledgeConfig(I2C_USED, DISABLE);
    I2C_GenerateSTOP(I2C_USED, ENABLE);
    /* Wait until one byte has been received */
    while( !I2C_CheckEvent(I2C_USED, I2C_EVENT_MASTER_BYTE_RECEIVED) ) {
        if (!(timeout--)) {
            temperature_callback(TEMP_ERROR);
        }
    }
    /* Read data from I2C data register and return data byte */
    unsigned char data = (unsigned char) I2C_ReceiveData(I2C_USED);
    return data;
}

static unsigned char i2c_send_byte(const unsigned char ucdata)
{
     unsigned long timeout = TEMPERATURE_RETRY;
    I2C_AcknowledgeConfig(I2C_USED, ENABLE);
    /* Wait for I2C EV8 --> last byte is still being transmitted (last byte in SR, buffer empty), next byte can already be written */
    while(!I2C_CheckEvent(I2C_USED, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {
        if (!(timeout--)) {
            temperature_callback(TEMP_ERROR);
        }
    }
    I2C_SendData(I2C_USED, ucdata);
    while(!I2C_CheckEvent(I2C_USED, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {
        if (!(timeout--)) {
            temperature_callback(TEMP_ERROR);
        }
    }
    return 1;
}

void i2c_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef I2C_InitStruct;

    RCC_APB1PeriphClockCmd(I2C_CLK, ENABLE);
    RCC_APB2PeriphClockCmd(I2C_GPIO_CLK, ENABLE);

    /*
     * setup SCL and SDA pins
     */
    GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
    GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStructure);

    /* configure I2C */
    I2C_InitStruct.I2C_ClockSpeed = I2C_CLOCK_HZ;
    I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStruct.I2C_OwnAddress1 = 0x00;
    I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; /* Enable ACK as default */
    I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_Init(I2C_USED, &I2C_InitStruct);

    I2C_Cmd(I2C_USED, ENABLE);
}

void i2c_close(void)
{
    I2C_DeInit(I2C_USED);
}

static void i2c_start(unsigned char address, enum X_I2C_MODE mode)
{
    unsigned long timeout = TEMPERATURE_RETRY;
    /* Wait until I2C is not busy any more */
    while (I2C_GetFlagStatus(I2C_USED, I2C_FLAG_BUSY)) {
        if (!(timeout--)) {
            temperature_callback(TEMP_ERROR);
        }
    }

    /* Send START condition */
    I2C_GenerateSTART(I2C_USED, ENABLE);

    /* Wait for I2C1 EV5 --> Slave has acknowledged start condition */
    while (!I2C_CheckEvent(I2C_USED, I2C_EVENT_MASTER_MODE_SELECT)) {
        if (!(timeout--)) {
            temperature_callback(TEMP_ERROR);
        }
    }

    /* Send slave Address for write
     * Wait for I2C EV6, check if
     * either Slave has acknowledged Master transmitter or
     * Master receiver mode, depending on the transmission
     * direction
     */
    if (mode == READ) {
        I2C_Send7bitAddress(I2C_USED, address, I2C_Direction_Receiver);
        while(!I2C_CheckEvent(I2C_USED, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) {
            if (!(timeout--)) {
                temperature_callback(TEMP_ERROR);
            }
        }
    } else if (mode == WRITE) {
        I2C_Send7bitAddress(I2C_USED, address, I2C_Direction_Transmitter);
        while(!I2C_CheckEvent(I2C_USED, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
            if (!(timeout--)) {
                temperature_callback(TEMP_ERROR);
            }
        }
    } else {
        /* Call user error handler */
    }
}

unsigned long
i2c_read(unsigned char slave_address, unsigned char from_address, unsigned long length, unsigned char *pdata)
{
    unsigned long count = 0;
    i2c_start(slave_address << 1, WRITE);
    i2c_send_byte(from_address);
    i2c_stop(slave_address);
    if (!length) {
        return 0;
    } else if (length == 1) {
        i2c_start(slave_address << 1, READ);
        *pdata = i2c_receive_byte_nack();
        /* i2c_stop(slave_address); */
        count++;
    } else {
        i2c_start(slave_address << 1, READ);
        for (count = 1; count < length; count++) {
            *pdata++ = i2c_receive_byte_ack();
        }
        /* Last byte */
        *pdata = i2c_receive_byte_nack();
        /* i2c_stop(slave_address); */
    }
    return count;
}

unsigned long
i2c_write(unsigned char slave_address, unsigned char from_address, unsigned long length, unsigned char *pdata)
{
    unsigned long count = 0;
    i2c_start(slave_address << 1, WRITE);
    i2c_send_byte(from_address);
    if (!length) {
        return 0;
    } else {
        for (count = 0; count < length; count++) {
            i2c_send_byte(*pdata++);
        }
    }
    i2c_stop(slave_address);
    return count;
}


/* 
 * For LM35
 */
static void adc_init(void)
{
    ADC_InitTypeDef       ADC_InitStructure;
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
    DMA_InitTypeDef       DMA_InitStructure;
    GPIO_InitTypeDef      GPIO_InitStructure;

    /* Enable ADC3, DMA2 and GPIO clocks ****************************************/
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOC, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);

    /* DMA2 Stream0 channel0 configuration **************************************/
    DMA_InitStructure.DMA_Channel = DMA_Channel_2;  
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC3_DR_ADDRESS;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&adc_value;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_InitStructure.DMA_BufferSize = 1;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA2_Stream0, &DMA_InitStructure);
    DMA_Cmd(DMA2_Stream0, ENABLE);

    /* Configure ADC3 Channel12 pin as analog input ******************************/
    GPIO_InitStructure.GPIO_Pin = ADC_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
    GPIO_Init(ADC_GPIO_PORT, &GPIO_InitStructure);

    /* ADC Common Init **********************************************************/
    ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
    ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
    ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
    ADC_CommonInit(&ADC_CommonInitStructure);

    /* ADC3 Init ****************************************************************/
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfConversion = 1;
    ADC_Init(ADC3, &ADC_InitStructure);

    /* ADC3 regular channel12 configuration *************************************/
    ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_3Cycles);

    /* Enable DMA request after last transfer (Single-ADC mode) */
    ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);

    /* Enable ADC3 DMA */
    ADC_DMACmd(ADC3, ENABLE);

    /* Enable ADC3 */
    ADC_Cmd(ADC3, ENABLE);
    
    /* Start ADC3 Software Conversion */ 
    ADC_SoftwareStartConv(ADC3);
}
/*=============================================================================*/
/* APIs */

void temperature_open(struct xtemperature *temperature)
{
#ifdef LM35
    adc_init();
#else
    i2c_init();
    temperature_callback = temperature->temp_error_handler;
    i2c_write(TEMPERATURE_ADDR, CONFIG_REG, 1, 0x00);
#endif
}

unsigned char temperature_get(struct xtemperature *temperature)
{
    unsigned char buf[DATA_SIZE];
    unsigned long temp;
#ifdef LM35
    temp = 100*(adc_value * 3300/0xFFF);
    temperature->temparature_round = temp;
    temperature->temparature_half = 0;
#else
    i2c_read(TEMPERATURE_ADDR, TEMP_REG, DATA_SIZE, buf);
    temp = (((buf[0] << 8) | buf[1]) >> 7) & 0xFF;
    if (temp & 0x01){
        temperature->temparature_half = 1;
    } else {
        temperature->temparature_half = 0;
    }
    temperature->temparature_round = temp >> 1;
#endif
    return 1;
}

unsigned char temperature_set(struct xtemperature *temperature)
{
    return 1;
}

void temperature_close(void)
{
    i2c_close();
}

